using System.Runtime.InteropServices;
using Unity.Collections.LowLevel.Unsafe;
using Unity.Collections;
using UnityEngine.Serialization;

namespace DA.AnimGraph {
    public unsafe struct UpstreamData {
        public UnsafeList<TimelineSlice> TimelineSlices => timelineSlicesInternal;
        private UnsafeList<TimelineSlice> timelineSlicesInternal;

        public UnsafeList<SyncGroup> SyncGroups => syncGroupsInternal;
        private UnsafeList<SyncGroup> syncGroupsInternal;

        public UpstreamData(int timelineCapacity) {
            timelineSlicesInternal = new(timelineCapacity, Allocator.Persistent);
            syncGroupsInternal = new(2, Allocator.Persistent);
        }

        /// <summary>
        /// 添加正常时间片
        /// </summary>
        public void AddTimelineSlice(AUID uuid, in TimeState* timeState, bool onEnter) {
            var s = new TimelineSlice(uuid, timeState, onEnter);
            timelineSlicesInternal.Add(s);
        }

        /// <summary>
        /// 添加动画退出时间片
        /// </summary>
        public void AddTimelineEndSlice(AUID uuid, in TimeState* timeState) {
            var s = new TimelineSlice(uuid, timeState->duration);
            timelineSlicesInternal.Add(s);
        }

        public void AddSyncClip(AUID groupID, in SyncClipInfo syncClipInfo) {
            for (int i = 0; i < syncGroupsInternal.Length; i++) {
                ref var group = ref syncGroupsInternal.GetRefAt(i);
                if (group.groupID == groupID) {
                    group.AddSyncClip(syncClipInfo);
                    return;
                }
            }
            {
                var group = new SyncGroup(groupID);
                group.AddSyncClip(syncClipInfo);
                syncGroupsInternal.Add(group);
            }
        }

        public void Reset() {
            timelineSlicesInternal.Clear();
            syncGroupsInternal.Clear();
        }

        public void Dispose() {
            timelineSlicesInternal.Dispose();
            syncGroupsInternal.Dispose();
        }

    }

    public unsafe struct TimelineSlice {
        public readonly AUID uuid;
        readonly TimeState* timeState;
        public float startTime { get; private set; } // 起止时间都是百分比形式
        public float endTime { get; private set; }
        public float duration => isExit ? startTime : timeState->duration;
        public bool isExit => timeState == null;
        [MarshalAs(UnmanagedType.U1)] public bool isEnter;
        public TimelineSlice(AUID uuid, in TimeState* timeState, bool isEnter) {
            this.uuid = uuid;
            this.timeState = timeState;
            this.isEnter = isEnter;
            startTime = -1;
            endTime = -1;
        }

        // 表示终止，没有timeState了
        public TimelineSlice(AUID uuid, float duration) {
            this.uuid = uuid;
            timeState = null; // 表示退出该动作
            startTime = duration; // 借用startTime保存动画片段时长
            endTime = -1;
            isEnter = false;
        }

        /// <summary>
        /// 是否在时间片内
        /// </summary>
        public bool IsInSlice(float time) {
            if (isExit) return false;
            Solve();

            if (startTime == endTime && time == endTime) {
                return true;
            }
            if (time >= startTime && time < endTime) {
                return true;
            }
            if (timeState->isLoop && startTime < 0) {
                float duration = /*timeState->mode == TimeState.AccumulateMode.Time*/ true ? timeState->duration : 1;
                time -= duration;
                if (time >= startTime && time < endTime) {
                    return true;
                }
            }
            return false;
        }

        /// <summary>
        /// 如果刚进入，且开始时间不是0，可能会跳过一些事件
        /// 在IsInSlice之后补充检查
        /// 这里追加检查是否在0到end范围内(虽然start到end已经查过了)
        /// </summary>
        public bool IsInSliceIfOnEnter(float time) {
            if (!isEnter) return false; // 不是刚进入就算不在范围内
            if (isExit) return false;
            Solve();

            if (time >= 0 && time < endTime) {
                return true;
            }
            return false;
        }

        /// <summary>
        /// 延迟计算时间片的起止点
        /// </summary>
        public void Solve() {
            if (isExit) return;
            if (endTime >= 0) return; // 已经算过就直接退出

            float baseTime = timeState->baseTime;
            float time = timeState->time;
            float duration = 0;

            if (!timeState->isLoop) {
                duration = timeState->duration;
                startTime = baseTime / duration;
                endTime = time / duration;
            } else {
                if (timeState->mode == TimeState.AccumulateMode.Time) {
                    duration = timeState->duration;
                    startTime = baseTime / duration % 1;
                    endTime = time / duration % 1;
                } else {
                    duration = 1;
                    startTime = baseTime % duration;
                    endTime = time % duration;
                }

                // 循环跨边界时，让起点变成负的
                if (startTime > endTime) {
                    startTime -= 1;
                }
            }

        }
    }

    public struct SyncGroup {
        public readonly AUID groupID;
        public UnsafeList<SyncClipInfo> SyncClipInfos => syncClipInfosInternal;
        private UnsafeList<SyncClipInfo> syncClipInfosInternal;

        public int leaderEnterCounter { get; private set; }
        public int leaderIndex { get; private set; }
        public int Count => syncClipInfosInternal.Length;

        public SyncGroup(AUID groupID) {
            this.groupID = groupID;
            syncClipInfosInternal = new UnsafeList<SyncClipInfo>(2, Allocator.Temp);
            leaderEnterCounter = 0;
            leaderIndex = 0;
        }

        public void AddSyncClip(in SyncClipInfo info) {
            int index = syncClipInfosInternal.Length;
            syncClipInfosInternal.Add(info);
            if (syncClipInfosInternal.Length == 1) {
                leaderIndex = index;
                leaderEnterCounter = info.enterEvaluateCounter;
                return;
            }
            if (info.enterEvaluateCounter < leaderEnterCounter) {
                leaderEnterCounter = info.enterEvaluateCounter;
                leaderIndex = index;
            }
        }
    }

    public unsafe struct SyncClipInfo {
        public TimeState* timeState;
        public int enterEvaluateCounter;
    }
}